# +----------------------------------------------------------------------
# | ChatWork智能聊天办公系统
# +----------------------------------------------------------------------
# | 软件声明: 本系统并非自由软件,未经授权任何形式的商业使用均属非法。
# | 版权保护: 任何企业和个人不允许对程序代码以任何形式任何目的复制/分发。
# | 授权要求: 如有商业使用需求,请务必先与版权所有者取得联系并获得正式授权。
# +----------------------------------------------------------------------
# | Author: ChatWork Team <2474369941@qq.com>
# +----------------------------------------------------------------------
import json
import time
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from tortoise.models import in_transaction
from common.utils.times import TimeUtil
from common.enums.pay import PayEnum
from common.enums.wallet import WalletEnum
from common.models.users import UserModel
from common.models.users import UserWalletModel
from common.models.users import UserSubscribeModel
from common.models.market import RechargeOrderModel
from common.models.market import SubscribeOrderModel


class PayNotifyService:
    """ 支付回调服务类 """

    @classmethod
    async def handle(cls, action: str, order_sn: str, transaction_id: str = ""):
        async with in_transaction("mysql"):
            if action == "recharge":
                await cls.recharge(order_sn, transaction_id)
            elif action == "subscribe":
                await cls.subscribe(order_sn, transaction_id)

    @classmethod
    async def recharge(cls, order_sn: str, transaction_id: str = ""):
        """
        充值回调处理

        Args:
            order_sn: str (订单编号)
            transaction_id: str (支付编号)

        Author:
            zero
        """
        # 查询订单
        order = await RechargeOrderModel.filter(order_sn=order_sn).first()

        # 查询用户
        user = await UserModel.filter(id=order.user_id).first()

        # 增加余额
        user.points += order.points_num
        user.train_num += order.train_num
        user.robot_num += order.robot_num
        user.know_num += order.know_num
        user.mcp_num += order.mcp_num
        await user.save()

        # 更新状态
        order.pay_time = int(time.time())
        order.pay_status = PayEnum.PAID_OK
        order.transaction_id = transaction_id
        await order.save()

        # 流水流程
        flowing = [
            {"num": order.points_num, "scene": WalletEnum.POINTS, "type": WalletEnum.POINTS_INC_RECHARGE},
            {"num": order.train_num, "scene": WalletEnum.TRAIN, "type": WalletEnum.TRAIN_INC_RECHARGE},
            {"num": order.robot_num, "scene": WalletEnum.ROBOT, "type": WalletEnum.ROBOT_INC_RECHARGE},
            {"num": order.robot_num, "scene": WalletEnum.KNOW, "type": WalletEnum.KNOW_INC_RECHARGE},
            {"num": order.robot_num, "scene": WalletEnum.MCP, "type": WalletEnum.MCP_INC_RECHARGE}
        ]

        # 记录流水
        for flow in flowing:
            if flow["num"] <= 0:
                continue
            await UserWalletModel.inc(
                user_id=user.id,
                scene=flow["type"],
                change_type=flow["type"],
                change_amount=flow["num"],
                source_id=order.id,
                source_sn=order.order_sn,
                project="账户充值",
                remarks=WalletEnum.get_change_type_msg(flow["type"])
            )

    @classmethod
    async def subscribe(cls, order_sn: str, transaction_id: str = ""):
        # 当前时间
        currentTime = int(time.time())

        # 查询订单
        order = await SubscribeOrderModel.filter(order_sn=order_sn).first()

        # 查询订阅
        member = await UserSubscribeModel.filter(user_id=order.user_id, package_id=order.package_id).first()

        # 查询用户
        user = await UserModel.filter(id=order.user_id).first()

        # 套餐信息
        snapshot = json.loads(order.snapshot)
        calendar = str(snapshot.get("calendar"))
        duration = int(snapshot.get("duration"))
        give_points = int(snapshot.get("give_points"))

        # 计算时长
        is_perpetual = 0  # 是否永久
        add_ren_time = 0  # 续费时间
        vip_end_time = 0  # 到期时间
        if calendar == "day":
            dt = datetime.fromtimestamp(currentTime)
            dt = dt + timedelta(days=duration)
            add_ren_time = int(dt.timestamp())
        elif calendar == "month":
            dt = datetime.fromtimestamp(currentTime)
            dt = dt + relativedelta(months=duration)
            add_ren_time = int(dt.timestamp())
        elif calendar == "year":
            dt = datetime.fromtimestamp(currentTime)
            dt = dt + relativedelta(years=duration)
            add_ren_time = int(dt.timestamp())
        elif calendar == "perpetual":
            is_perpetual = 1

        # 旧套餐延迟生效
        if not is_perpetual:
            # 查有效期记录
            effective = await (UserSubscribeModel
                               .filter(is_delete=0)
                               .filter(user_id=order.user_id)
                               .filter(expire_time__gt=int(time.time()))
                               .order_by("effect_time")
                               .all())

            # 上个套餐的结束时间
            eff_end_time = add_ren_time
            if member and not member.is_perpetual:
                if member.expire_time > currentTime:
                    eff_end_time = member.expire_time - currentTime + add_ren_time

            # 会员延迟生效
            for index, eff in enumerate(effective):
                if member and member.id == eff.id:
                    continue
                if eff.is_perpetual:
                    break

                remain_time = TimeUtil.compute_remain_time(eff.expire_time)
                if eff.remain_time > 0:
                    eff.expire_time = eff_end_time + eff.remain_time
                else:
                    eff.remain_time = remain_time
                    eff.expire_time = eff_end_time + remain_time

                eff.update_time = currentTime
                eff.effect_time = eff_end_time
                eff_end_time = eff.expire_time
                await eff.save()

        # 更新订阅
        if not member:
            # 升级套餐
            await UserSubscribeModel.create(
                user_id=order.user_id,
                package_id=order.package_id,
                pricing_id=order.pricing_id,
                is_perpetual=is_perpetual,
                expire_time=add_ren_time,
                effect_time=currentTime,
                create_time=currentTime,
                update_time=currentTime,
                snapshot=json.dumps(snapshot, ensure_ascii=False)
            )
        else:
            # 续费套餐
            if member.is_perpetual:
                is_perpetual = 1
            else:
                vip_end_time = add_ren_time
                if member.expire_time > currentTime:
                    vip_end_time = member.expire_time - currentTime + add_ren_time

            snapshot["is_perpetual"] = is_perpetual
            snapshot["vip_end_time"] = 0 if is_perpetual else vip_end_time
            await UserSubscribeModel.filter(id=member.id).update(
                user_id=order.user_id,
                package_id=order.package_id,
                pricing_id=order.pricing_id,
                is_perpetual=is_perpetual,
                expire_time=0 if is_perpetual else vip_end_time,
                renew_time=currentTime,
                update_time=currentTime,
                snapshot=json.dumps(snapshot, ensure_ascii=False)
            )

        # 更新订单
        order.transaction_id = transaction_id
        order.pay_status = PayEnum.PAID_OK
        order.pay_time = currentTime
        order.package_end_time = 0 if is_perpetual else vip_end_time
        await order.save()

        # 赠送积分
        if give_points > 0:
            user.points += give_points
            await user.save()
            await UserWalletModel.inc(
                user_id=user.id,
                scene=WalletEnum.POINTS,
                change_type=WalletEnum.POINTS_INC_SUBSCRIBE,
                change_amount=give_points,
                source_id=order.id,
                source_sn=order.order_sn,
                project="订阅会员",
                remarks=WalletEnum.get_change_type_msg(WalletEnum.POINTS_INC_SUBSCRIBE)
            )
